#include "qb_modules.h"

#include <core/QbLogger.h>

#include "core/system/utf_util_win.h"
#include "qbmessage/include/message_kernel.h"
#include "qbnet/include/network_service.h"

namespace qb {
ComItem QbModules::m_comDatabase[] = {
    {_T("qbmessage.dll"), CLSID_MessageKernel, NULL},
    {_T("qbnet.dll"), CLSID_NetworkService, NULL},
};

QbModules::QbModules() {}

QbModules::~QbModules() { assert(m_coms.size() == 0); }

int32_t QbModules::LoadResource(const char* exepath) { return 0; }

int32_t QbModules::UnloadResource() {
  if (m_coms.size() == 0) {
    log_warning("resource size is 0");
    return -1;
  }

  // 第一次遍历：释放那些未被其他对象包含的对象
  // 第二次遍历：释放那些未释放的对象, 并且卸载相应的模块
  // 之所以两次循环，是为了处理相互包含的情况
  for (Coms::iterator it = m_coms.begin(); it != m_coms.end(); ++it) {
    for (ComObjs::iterator it_obj = it->second.objs.begin();
         it_obj != it->second.objs.end(); ++it_obj) {
      IUnknown* fac = static_cast<IUnknown*>(it_obj->second.ppv);
      if (fac) {
        int32_t uRef = fac->Release();
        if (uRef == 0) {
          // 对象已经完全释放
          it_obj->second.ppv = NULL;
        } else {
          // 说明还在使用暂时还不能释放
          fac->AddRef();
        }
      } else {
        assert(0);
      }
    }
  }
  for (Coms::iterator it = m_coms.begin(); it != m_coms.end(); ++it) {
    for (ComObjs::iterator it_obj = it->second.objs.begin();
         it_obj != it->second.objs.end(); ++it_obj) {
      IUnknown* fac = static_cast<IUnknown*>(it_obj->second.ppv);
      if (fac) {
        while (fac->Release() > 0)
          ;
      }
    }

    it->second.com->Unload();
  }

  m_coms.clear();

  return 0;
}

int32_t QbModules::QueryInterface(REFCLSID rclsid, REFIID riid, void** ppv) {
  Coms::iterator it_clsid = m_coms.find(rclsid);
  if (it_clsid == m_coms.end()) {
    for (int32_t i = 0; i < kMaxNumberCom; i++) {
      if (rclsid == m_comDatabase[i].clsid) {
        QbCom* com = new QbCom();
        int32_t ret = com->Load(m_comDatabase[i].name.c_str());
        if (ret == 0) {
          ComItem item = m_comDatabase[i];
          item.com = com;
          m_coms[m_comDatabase[i].clsid] = item;
          it_clsid = m_coms.find(rclsid);
          break;
        } else {
          delete com;
          //	assert(0);
          return -1;
        }
      }
    }
  }

  if (it_clsid != m_coms.end()) {
    ComObjs::iterator it_riid = it_clsid->second.objs.find(riid);
    if (it_riid == it_clsid->second.objs.end()) {
      if (it_clsid->second.com->CreateInstance(rclsid, riid, ppv) == 0) {
        ComObjItem item;
        item.riid = riid;
        item.ppv = *ppv;
        ((IUnknown*)item.ppv)->AddRef();
        it_clsid->second.objs[riid] = item;
        return 0;
      } else {
        assert(0);
      }
    } else if (SUCCEEDED(((IUnknown*)it_riid->second.ppv)
                             ->QueryInterface(riid, (void**)&*ppv))) {
      return 0;
    } else if (!it_riid->second.ppv) {
      assert(0);
    }
  }

  assert(0);
  return -1;
}

}  // namespace qb
